#include "procinfomodel.h"

#include <QDir>
#include <QStandardPaths>
#include <QProcess>

ProcInfoModel::ProcInfoModel(QObject *parent)
    : QObject(parent)
    , m_netMonitorInter(nullptr)
    , m_enableNetDataMonitor(true)
    , m_showNetUnKnownItem(false)
{
    initData();
}

ProcInfoModel::~ProcInfoModel()
{
}

void ProcInfoModel::initData()
{
    registerProcInfoMetaType();
    registerProcInfoListMetaType();

    m_netMonitorInter = new QDBusInterface(CCC_SYS_DAEMON_DBUS_NAME,
                                           CCC_SYS_DAEMON_DBUS_PATH,
                                           CCC_SYS_DAEMON_DBUS_IFC,
                                           QDBusConnection::systemBus(), this);

    loadCfgs();

    checkAndNotifyInstallCccSysDaemon();
}

bool ProcInfoModel::loadCfgsFromFilePath(const QString &path)
{
    QFile file(path);
    if (!file.open(QIODevice::OpenModeFlag::ReadOnly)) {
        qWarning() << Q_FUNC_INFO << path << "open failed";
        file.close();
        return false;
    }

    QByteArray content = file.readAll();
    file.close();
    QJsonParseError err;
    QJsonDocument doc = QJsonDocument::fromJson(content, &err);
    if (err.error != QJsonParseError::NoError) {
        qCritical() << Q_FUNC_INFO << "parse json failed";
        return false;
    }

    QJsonObject mainJsonObj = doc.object();
    QJsonObject netJsonObj = mainJsonObj.take(CFG_KEY_NET).toObject();

    m_enableNetDataMonitor = netJsonObj.value(CFG_KEY_NET_DATA_MONITOR_SWITCH).toBool();
    m_showNetUnKnownItem = netJsonObj.value(CFG_KEY_NET_SHOW_NET_UNKNOWN_ITEM_SWITCH).toBool();

    return true;
}

void ProcInfoModel::loadCfgs()
{
    if (!loadCfgsFromFilePath(CfgFilePath)) {
        loadCfgsFromFilePath(DefaultCfgFilePath);
    }
}

void ProcInfoModel::saveCfgs()
{
    QFileInfo fInfo(CfgFilePath);
    if (!fInfo.dir().exists()) {
        fInfo.dir().mkdir(fInfo.dir().path());
    }

    // 先读取配置内容
    QJsonObject mainJsonObj;
    QFile file(CfgFilePath);
    if (file.open(QIODevice::OpenModeFlag::ReadOnly)) {
        QByteArray content = file.readAll();
        file.close();
        QJsonParseError err;
        QJsonDocument doc = QJsonDocument::fromJson(content, &err);
        if (err.error != QJsonParseError::NoError) {
            qWarning() << Q_FUNC_INFO << "parse json failed";
        }
        // 转成主json对象
        mainJsonObj = doc.object();
        file.close();
    }

    QJsonObject netJsonObj;
    netJsonObj.insert(CFG_KEY_NET_DATA_MONITOR_SWITCH, m_enableNetDataMonitor);
    netJsonObj.insert(CFG_KEY_NET_SHOW_NET_UNKNOWN_ITEM_SWITCH, m_showNetUnKnownItem);
    mainJsonObj.insert(CFG_KEY_NET, netJsonObj);

    QJsonDocument chandedDoc(mainJsonObj);
    QByteArray changedContent = chandedDoc.toJson();

    if (!file.open(QIODevice::OpenModeFlag::WriteOnly)) {
        qWarning() << Q_FUNC_INFO << CfgFilePath << "open failed";
        file.close();
        return;
    }

    qint64 ret = file.write(changedContent);
    if (-1 == ret) {
        qWarning() << Q_FUNC_INFO << CfgFilePath << "write failed";
        file.close();
        return;
    }
    file.close();
}

bool ProcInfoModel::isPkgInstalled(const QString &pkgName)
{
    QProcess proc;
    proc.start("dpkg", {"--status", pkgName});
    proc.waitForFinished();

    QString replyContent = proc.readAllStandardOutput();
    proc.close();

    QStringList lineContentStrList = replyContent.split("\n");
    QString pkgStatusLineStr;
    for (QString lineStr : lineContentStrList) {
        if (lineStr.startsWith("Status:")) {
            pkgStatusLineStr = lineStr;
            break;
        }
    }

    if (pkgStatusLineStr.isEmpty()) {
        return false;
    }

    return !pkgStatusLineStr.contains("deinstall");
}

void ProcInfoModel::checkAndNotifyInstallCccSysDaemon()
{
    if (isPkgInstalled(CccSysDaemonPkgName)) {
        return;
    }

    DDialog dlg;
    dlg.setMessage("检测到未安装服务，是否开始安装服务？");
    dlg.addButtons({"是", "否"});
    int ret = dlg.exec();
    qInfo() << Q_FUNC_INFO << ret;
    if (ret != 0) {
        return;
    }

    // 安装服务
    QProcess proc;
    proc.start("pkexec", {"dpkg", "-i", CccSysDaemonPkgFilePath});
    proc.waitForFinished();

    QString errMsg = proc.readAllStandardError();
    proc.close();

    if (!errMsg.isEmpty()) {
        DDialog errDlg;
        errDlg.setMessage("安装失败：" + errMsg);
        errDlg.exec();
        return;
    }

    DDialog successDlg;
    successDlg.setMessage("安装成功");
    successDlg.addButton("确定");
    successDlg.exec();
}

bool ProcInfoModel::isEnableNetDataMonitor()
{
    return m_enableNetDataMonitor;
}

void ProcInfoModel::enableNetDataMonitor(bool enable)
{
    m_enableNetDataMonitor = enable;
    m_netMonitorInter->call(QDBus::Block, "EnableNetFlowMonitor", enable);
    saveCfgs();
    Q_EMIT netDataMonitorSwitchChanged(m_enableNetDataMonitor);
}

bool ProcInfoModel::isShowNetUnKnownItem()
{
    return m_showNetUnKnownItem;
}

void ProcInfoModel::showNetUnKnownItem(bool show)
{
    m_showNetUnKnownItem = show;
    saveCfgs();
    Q_EMIT showNetUnKnownItemSwitchChanged(m_showNetUnKnownItem);
}

ProcInfoList ProcInfoModel::getProcInfoList()
{
    QDBusReply<ProcInfoList> reply = m_netMonitorInter->call(QDBus::CallMode::Block, "GetProcInfoList");
    if (!reply.isValid()) {
        qDebug() << Q_FUNC_INFO << reply.error();
        return ProcInfoList{};
    }
    return reply.value();
}

void ProcInfoModel::createProcCleanerDesktop()
{
    QString srcFilePath = "/opt/apps/com.github.ccc-proc-info-plugin/files/Tools/CccProcCleaner.desktop";
    QString destFilePath = QString("%1/%2")
            .arg(QStandardPaths::writableLocation(QStandardPaths::StandardLocation::DesktopLocation))
            .arg("CccProcCleaner.desktop");

    bool ret = QFile::copy(srcFilePath, destFilePath);
    if (!ret) {
        qWarning() << Q_FUNC_INFO << "create Proc Cleaner Desktop failed!";
    }
}
